// abstract 抽象类（不能被实例化，只能被继承）
abstract class Heart {
  ws?: WebSocket;

  isReceivePong = false; // 是否收到pong信号

  /*
    开始心跳检测： 
      1. 每隔一段时间5s，向ws服务器发送一个 ping 信号
      2. 在规定时间内2s，检查ws服务器是否返回了 pong 信号
        如果返回了，说明连接成功，继续下一次心跳检查
        如果没有返回，说明链接断开了，开始重连
  */
  startHeart() {
    // 使用 addEventListener 绑定 message 事件（绑定事件回调函数可以n个，使用onmessage只能绑定一个）
    this.ws!.addEventListener("message", (e) => {
      const data = e.data;
      if (data === "pong") {
        this.isReceivePong = true;
      }
    });

    this.hearting();
  }

  // hearting: 心跳中
  hearting() {
    setTimeout(() => {
      // 向ws服务器发送一个 ping 信号
      this.ws!.send("ping");
      setTimeout(() => {
        // 检测是否收到 pong 信号
        if (this.isReceivePong) {
          console.log("心跳检查成功~");
          // OK，继续下一次心跳检查
          this.isReceivePong = false;
          this.hearting();
        } else {
          console.log("心跳检查失败~");
          // 不OK，重连
          // 断开链接 -> 触发重连
          this.ws!.close();
        }
      }, 2000);
    }, 5000);
  }
}

export default class Socket extends Heart {
  url: string = "";
  ws: WebSocket | undefined = undefined;

  MAX_RECONNECT_NUM = 5; // 最大重连次数
  currentReconnectNum = 0; // 当前重连次数

  callbacks: Function[] = [];

  constructor(url: string) {
    super(); // 继承必须调用super
    this.url = url;
    this.init();
  }

  // 初始化 -> 连接WebSocket服务
  init() {
    const ws = new WebSocket(this.url);
    // 监视是否连接成功的事件
    ws.onopen = this.handleOpen.bind(this); // 修改this指向
    // 监视是否连接断开的事件
    ws.onclose = this.handleClose.bind(this);

    this.ws = ws;

    // 处理消息
    this.handleMessage();
  }

  // 处理接受消息
  handleMessage() {
    this.ws!.addEventListener("message", (e) => {
      const data = e.data;
      if (data === "pong") return;
      this.callbacks.forEach((cb) => cb(JSON.parse(data)));
    });
  }
  // 处理ws是否连接成功的事件回调函数
  handleOpen() {
    console.log("open");
    this.currentReconnectNum = 0;
    // 连接成功时，开始进行心跳检测
    this.startHeart();
  }
  // 处理ws是否连接断开的事件回调函数
  handleClose() {
    console.log("close", this.currentReconnectNum);
    this.reconnect();
  }

  // 重连
  reconnect() {
    if (this.currentReconnectNum === this.MAX_RECONNECT_NUM) {
      // 超过重连最大次数
      console.log("重连失败");
      return;
    }

    // 重连次数++
    this.currentReconnectNum++;
    setTimeout(() => {
      this.init!();
    }, 1000);
  }

  // 处理消息
  onmessage(cb: Function) {
    this.callbacks.push(cb);
  }
}

// // 将来使用：
// const ws = new Socket("ws://localhost:8081");

// ws.onmessage((data: any) => {
//   console.log("接受到消息1", data);
// });

// ws.onmessage((data: any) => {
//   console.log("接受到消息2", data);
// });

// ws.onmessage((data: any) => {
//   console.log("接受到消息3", data);
// });
